package com.adcc.loadsheet.billing.filter;

import com.adcc.loadsheet.billing.util.MD5Util;
import com.alibaba.fastjson.JSON;
import com.google.common.base.Strings;
import com.google.common.collect.Maps;
import org.apache.commons.codec.digest.DigestUtils;
import org.springframework.util.StreamUtils;

import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.Part;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.Charset;
import java.util.Locale;
import java.util.Map;

/**
 * 为了解决获取输入流导致参数丢失的解决方案
 * Created by zhaoml on 2020-04-01.
 */
public class ParameterCheckServletRequestWrapper extends HttpServletRequestWrapper {
    private byte[] requestBody;
    private Charset charSet;
    private boolean hasError;

    public boolean isHasError() {
        return hasError;
    }

    public ParameterCheckServletRequestWrapper(HttpServletRequest request) {
        super(request);
        try {
            requestBody = StreamUtils.copyToByteArray(request.getInputStream());
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        Map<String, String[]> map = getParameterMap();
        if (map != null && !map.isEmpty()) {
            for (Map.Entry<String, String[]> entry : map.entrySet()) {
                String k = entry.getKey();
                String[] value = entry.getValue();
                if ("action".equals(k)) {
                    continue;
                }
                for (String v : value) {
                    if (hasInvalid(v)) {
                        hasError = true;
                        return;
                    }
                }
            }
        }
    }

    @Override
    public Map<String, String[]> getParameterMap() {
        String contentType = getContentType();
        if (requestBody != null && requestBody.length > 0 && getContentType() != null && contentType.contains("application/json")) {
            String json = new String(requestBody, 0, requestBody.length);
            Map<String, String[]> params = Maps.newHashMap();
            if (!Strings.isNullOrEmpty(json)) {
                Map<String, Object> tmp = JSON.parseObject(json, Map.class);
                tmp.forEach((k, v) -> params.put(k, new String[]{String.valueOf(v)}));
            }
            return params;
        } else if ("POST".equalsIgnoreCase(getMethod()) && contentType != null && contentType.toLowerCase(Locale.ENGLISH).startsWith("multipart/")) {
            Map<String, String[]> parameter = super.getParameterMap();
            parameter = parameter != null ? Maps.newHashMap(parameter) : Maps.newHashMap();
            try {
                int n = 0;
                for (Part part : getParts()) {
                    if (!Strings.isNullOrEmpty(part.getContentType()) && !Strings.isNullOrEmpty(part.getSubmittedFileName())) {
                        String md5 = part.getSize() > 0 ? MD5Util.MD5(part.getInputStream()) : DigestUtils.md5Hex(new byte[0]);
                        parameter.put("file" + (n == 0 ? "" : n), new String[]{md5});
                        n++;
                    }
                }
            } catch (Exception ex) {
                ex.printStackTrace();
            }
            return parameter;
        }
        return super.getParameterMap();
    }

    /**
     * 判断字符串中是否含有非法字符
     *
     * @param str
     * @return true  含有非法字符
     * false 没有非法字符
     */
    public static boolean hasInvalid(String str) {
        if (Strings.isNullOrEmpty(str))
            return false;
        str = str.toLowerCase(Locale.ENGLISH);
        if (str.equals("updatetime")) return false;
        if (str.equals("createtime")) return false;
        if (str.equals("updateuser")) return false;
        if (str.equals("createuser")) return false;
        if (str.equals("efbupdatecount")) return false;
        if (str.equals("updatecycle")) return false;
        if (str.equals("updatestate")) return false;
        if (str.equals("updatetype")) return false;
        if (str.equals("datacreatetime")) return false;
        if (str.equals("updatelog")) return false;
        if (str.equals("refpcreatetime")) return false;
        if (str.equals("pdfcreatetime")) return false;
        if (str.equals("updatelogid")) return false;
        if (str.equals("updateflag")) return false;
        if (str.indexOf("updateat") >= 0) return false;
        if (str.indexOf("createat") >= 0) return false;

        if (str.equals("and") || str.indexOf(" and ") >= 0) return true;
        if (str.equals("or") || str.indexOf(" or ") >= 0) return true;
        if (str.equals("truncate") || str.indexOf(" truncate ") >= 0) return true;
        if (str.equals("char") || str.indexOf(" char ") >= 0) return true;
        if (str.equals("declare") || str.indexOf(" declare ") >= 0) return true;
        if (str.equals("exec") || str.indexOf(" exec ") >= 0) return true;
        if (str.equals("count") || str.indexOf(" count ") >= 0) return true;
        if (str.equals("master") || str.indexOf(" master ") >= 0) return true;
        if (str.equals("chr") || str.indexOf(" chr ") >= 0) return true;
        if (str.equals("mid") || str.indexOf(" mid ") >= 0) return true;
        if (str.equals("join") || str.indexOf(" join ") >= 0) return true;
        if (str.indexOf(">") >= 0) return true;
        if (str.indexOf("<") >= 0) return true;
        if (str.indexOf("%") >= 0) return true;
        if (str.indexOf("'") >= 0) return true;
        if (str.indexOf(";") >= 0) return true;
        if (str.indexOf("*") >= 0) return true;
        if (str.indexOf("|") >= 0) return true;
        if (str.indexOf("\\u") >= 0) return true;
        if (str.indexOf("select") >= 0) return true;
        if (str.indexOf("insert") >= 0) return true;
        if (str.indexOf("update") >= 0) return true;
        if (str.indexOf("create") >= 0) return true;
        if (str.indexOf("delete") >= 0) return true;
        if (str.indexOf("drop") >= 0) return true;
        if (str.indexOf("0x0d") >= 0) return true;
        if (str.indexOf("0x0a") >= 0) return true;
        if (str.indexOf("\\t") >= 0) return true;
        return false;
    }

    public String getRequestPostStr(HttpServletRequest request)
            throws IOException {
        String charSetStr = request.getCharacterEncoding();
        if (charSetStr == null) {
            charSetStr = "UTF-8";
        }
        charSet = Charset.forName(charSetStr);

        return StreamUtils.copyToString(request.getInputStream(), charSet);
    }

    /**
     * 重写 getInputStream()
     */
    @Override
    public ServletInputStream getInputStream() {
        if (requestBody == null) {
            requestBody = new byte[0];
        }

        final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(requestBody);

        return new ServletInputStream() {
            @Override
            public boolean isFinished() {
                return false;
            }

            @Override
            public boolean isReady() {
                return false;
            }

            @Override
            public void setReadListener(ReadListener readListener) {

            }

            @Override
            public int read() {
                return byteArrayInputStream.read();
            }
        };
    }

    /**
     * 重写 getReader()
     */
    @Override
    public BufferedReader getReader() {
        return new BufferedReader(new InputStreamReader(getInputStream()));
    }
}